#include <cstring>
#include "base/cmd_flags.h"
#include "base/logging.h"
#include "strings/numbers.h"

DEFINE_int32(vlog, 3, "");

CmdFlags* CmdFlags::flags = NULL;

CmdFlags::CmdFlags() {
}
  
CmdFlags* CmdFlags::Get() {
  if (flags == NULL) {
    flags = new CmdFlags();
  }
  return flags;
}

void CmdFlags::AddInt(const string& name, int* i, const char* desc) {
  FlagValue* v = &allflags[name];
  v->type = FLAG_INT32;
  v->value = i;
  v->desc = string(desc);
}

void CmdFlags::AddInt64(const string& name, int64* i, const char* desc) {
  FlagValue* v = &allflags[name];
  v->type = FLAG_INT64;
  v->value = i;
  v->desc = string(desc);
}

void CmdFlags::AddDouble(const string& name, double* d, const char* desc) {
  FlagValue* v = &allflags[name];
  v->type = FLAG_DOUBLE;
  v->value = d;
  v->desc = string(desc);
}

void CmdFlags::AddBool(const string& name, bool* b, const char* desc) {
  FlagValue* v = &allflags[name];
  v->type = FLAG_BOOL;
  v->value = b;
  v->desc = string(desc);
}

void CmdFlags::AddString(const string& name, string* s, const char* desc) {
  FlagValue* v = &allflags[name];
  v->type = FLAG_STRING;
  v->value = s;
  v->desc = string(desc);
}

bool CmdFlags::GetFlag(const string& name, FlagValue** v) {
  map<string, FlagValue>::iterator iter = allflags.find(name);
  if (iter == allflags.end()) return false;
  *v = &iter->second;
  return true;
}
  
CmdFlagRegister::CmdFlagRegister(const string& name, int* i, int def, const char* desc) {
  CmdFlags::Get()->AddInt(name, i, desc);
  *i = def;
}

CmdFlagRegister::CmdFlagRegister(const string& name, bool* b, bool def, const char* desc) {
  CmdFlags::Get()->AddBool(name, b, desc);
  *b = def;
}

CmdFlagRegister::CmdFlagRegister(const string& name, string* s, const char* def, const char* desc) {
  CmdFlags::Get()->AddString(name, s, desc);
  *s = string(def);
}

CmdFlagRegister::CmdFlagRegister(const string& name, int64* i, int64 def, const char* desc) {
  CmdFlags::Get()->AddInt64(name, i, desc);
  *i = def;
}

CmdFlagRegister::CmdFlagRegister(const string& name, double* d, double def, const char* desc) {
  CmdFlags::Get()->AddDouble(name, d, desc);
  *d = def;
}

bool ParseFlags(int argc, char** argv) {
  CmdFlags* flags = CmdFlags::Get();
  for (int i = 1; i < argc; ++i) {
    const char* arg = argv[i];
    if (strlen(arg) < 2 || arg[0] != '-' || arg[1] != '-') {
      return false;
    }
    int separator = -1;
    for (int j = 2; j < strlen(arg); ++j) {
      if (arg[j] == '=') {
        separator = j;
        break;
      }
    }
    CHECK_GT(separator, 2);
    string name(arg + 2, separator - 2);
    string value(arg + separator + 1, strlen(arg) - separator - 1);
    FlagValue* flag_value;
    if (!flags->GetFlag(name, &flag_value)) return false;
    if (flag_value->type == FLAG_INT32) {
      int v;
      if (!strto32(value, &v)) return false;
      *((int*)flag_value->value) = v;
    } else if (flag_value->type == FLAG_INT64) {
      int64 v;
      if (!strto64(value, &v)) return false;
      *((int64*)flag_value->value) = v;
    } else if (flag_value->type == FLAG_DOUBLE) {
      double v;
      if (!strtod(value, &v)) return false;
      *((double*)flag_value->value) = v;
    } else if (flag_value->type == FLAG_STRING) {
      *((string*)flag_value->value) = value;
    } else if (flag_value->type == FLAG_BOOL) {
      if (value == "true") {
        *((bool*)flag_value->value) = true;
      } else if (value == "false") {
        *((bool*)flag_value->value) = false;
      } else {
        return false;
      }
    }
  }
  return true;
}
